/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef vendor_ecarx_xma_gnss_V1_0_Gnss_Ipcp_H_
#define vendor_ecarx_xma_gnss_V1_0_Gnss_Ipcp_H_

#include <thread>
#include <hardware/fused_location.h>
#include <hardware/gps.h>
#include <hidl/Status.h>
#include <ipcp/ipcp.h>
#include <vendor/ecarx/xma/iplm/1.0/IIplm.h>
#include <android/hardware/gnss/1.0/types.h>

namespace vendor {
namespace ecarx {
namespace xma {
namespace gnss {
namespace V1_0 {
namespace implementation {

using ::android::sp;
using ::android::wp;
using ::android::hardware::hidl_death_recipient;
using ::vendor::ecarx::xma::iplm::V1_0::IIplm;
using GnssLocation = android::hardware::gnss::V1_0::GnssLocation;
using GnssLocationFlags = android::hardware::gnss::V1_0::GnssLocationFlags;

typedef void (* gnssLocationCb)(GnssLocation* location);

class IplmRecipient : public hidl_death_recipient {
  public:
    IplmRecipient() {};
    ~IplmRecipient() {};

    void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) override;
};

/*
 * Represents the standard GPS interface. Also contains wrapper methods to allow methods from
 * GpsInterface interface to be passed into the conventional implementation of the GPS HAL.
 */
class GnssIpcp {
  public:
    GnssIpcp() {};
    ~GnssIpcp() {};

    static const GpsInterface* getGpsInterface();
    static int initGnssIpcp();
    static void releaseGnssIpcp();
    static void reportStatusToIplm();
    static void nmeaSetCallback(gnssLocationCb cb);

    /*
     * Holds function pointers to the ipcp_callbacks_t methods.
     */
    static ipcp_callbacks_t sIpcpCallbacks;
    static sp<IIplm> mIplm;
    static sp<IplmRecipient> sIplmRecipient;

  private:
    static std::thread mIpcpThread;
    static std::thread mIplmThread;
    static std::mutex sMutex;

    /*
     * Holds function pointers to the GpsInterface methods.
     */
    static GpsInterface sGpsInterface;

    /**
     * Opens the interface and provides the callback routines
     * to the implementation of this interface.
     */
    static int initGps(GpsCallbacks* callbacks);

    /** Starts navigating. */
    static int startGps( void );

    /** Stops navigating. */
    static int stopGps( void );

    /** Closes the interface. */
    static void cleanupGps( void );

    /** Injects the current time. */
    static int injectTimeGps(GpsUtcTime time, int64_t timeReference, int uncertainty);

    /**
     * Injects current location from another location provider (typically cell
     * ID). Latitude and longitude are measured in degrees expected accuracy is
     * measured in meters
     */
    static int injectLocationGps(double latitude, double longitude, float accuracy);

    /**
     * Specifies that the next call to start will not use the
     * information defined in the flags. GPS_DELETE_ALL is passed for
     * a cold start.
     */
    static void deleteAidingDataGps(GpsAidingData flags);

    /**
     * min_interval represents the time between fixes in milliseconds.
     * preferred_accuracy represents the requested fix accuracy in meters.
     * preferred_time represents the requested time to first fix in milliseconds.
     *
     * 'mode' parameter should be one of GPS_POSITION_MODE_MS_BASED
     * or GPS_POSITION_MODE_STANDALONE.
     * It is allowed by the platform (and it is recommended) to fallback to
     * GPS_POSITION_MODE_MS_BASED if GPS_POSITION_MODE_MS_ASSISTED is passed in, and
     * GPS_POSITION_MODE_MS_BASED is supported.
     */
    static int setPositionModeGps(GpsPositionMode mode, GpsPositionRecurrence recurrence,
            uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time);

    /** Get a pointer to extension information. */
    static const void* getExtensionGps(const char* name);

    /**
     * When STACK receives following IPCP packets, STACK would call this callback to notify caller.
     *      #REQUEST
     *      #RESPONSE
     *      #SETREQUEST
     *      #SETQUEST_NORETURN
     *      #ERROR
     *      #NOTIFICATION
     *      #NOTIFICATION_CYCLIC.
     *
     *  NOTE: Upper layer *MUST NOT* do any block operation in callback function.
     *        Upper layer *MUST memcpy this ipcp_packet*, because IPCP Stack would free
     *        the *ipcp_packet after this cb is called.
     */
    static void ipcpReceivedCallback(ipcp_packet_t* ipcp_packet, ipcp_peer_t peer);

    /**
     * When WFA/WFR Timeout occurres or some STACK error occurred,
     * STACK would notify caller via this function pointer.
     */
    static void ipcpStackErrorCallback(ipcp_packet_t* ipcp_packet, ipcp_stack_err_t error);

    static int setupIpcp();
    static void cleanupIpcp();

    /** this is the main thread, it waits for commands from gps_state_start/stop and,
    * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
    * that must be parsed to be converted into GPS fixes sent to the framework
    */
    static void gpsStateThread(void* arg);

    static void nmeaInit();
    static void nmeaParse(std::string nmeaValue, bool isLastEntry);

    static int nmeaUpdateTime(std::string utc_time);
    static int nmeaUpdateDate(std::string date, std::string utc_time);
    static void nmeaUpdateUtcDiff();
    static double convertFromHHMM(std::string value);

    static int nmeaUpdateLatlong(std::string latitude, std::string latitudeHemi,
                                  std::string longitude, std::string longitudeHemi);

    static int nmeaUpdateAltitude(std::string altitude, std::string aultitudeUnits);

    static int nmeaUpdateBearing(std::string bearing);

    static int nmeaUpdateSpeed(std::string speed);

    static int nmeaUpdateHorizontalAccuracy(std::string accuracy);
    static int nmeaUpdateVerticalAccuracy(std::string accuracy);

    static void nmeaDump();
    static void nmeaSvStatusDump();
};

}  // namespace implementation
}  // namespace V1_0
}  // namespace gnss
}  // namespace xma
}  // namespace ecarx
}  // namespace vendor

#endif  // vendor_ecarx_xma_gnss_V1_0_Gnss_Ipcp_H_
